# Dataspace

Сверхбыстрое, встраиваемое, персистентное хранилище типа "ключ-значение" на Go. 

## Концепция

В первую очередь срез байтов ключа преобразовывается в целочисленное значение с помощью самой быстрой на данный момент [хеш-функции](https://github.com/Cyan4973/xxHash). Далее ключ попадает в [двусвязный список](https://ru.wikipedia.org/wiki/%D0%A1%D0%B2%D1%8F%D0%B7%D0%BD%D1%8B%D0%B9_%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA), сохраняющий порядок записи, и, вместе со значением в [сбалансированное дерево](https://ru.wikipedia.org/wiki/%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE). Список позволяет итерировать элементы в порядке добавления, а дерево значительно ускоряет процесс чтения и записи. <br>
После сохранения в память данные записываются в файл по принципу wal или [упреждающей журнализации](https://ru.wikipedia.org/wiki/%D0%A3%D0%BF%D1%80%D0%B5%D0%B6%D0%B4%D0%B0%D1%8E%D1%89%D0%B0%D1%8F_%D0%B6%D1%83%D1%80%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F), сохраняя тем самым атомарность и устойчивость базы данных. При удалении или изменении данных файл не перезаписывается, данные всегда попадают в конец файла в виде потока байт. Каждая вставка имеет фракцию метаданных размером 8 байт для хранения информации о записи.

```sh
	    +------------+------------+----------------+-----------------+
имя     | режим      | статус     | размер ключа   | размер значения |
	    +------------+------------+----------------+-----------------+
размер  | 1          | 1          | 2              | 4               |
		+------------+------------+----------------+-----------------+
```
Для преобразования фракций в массивы байт и обратно используются самые быстрые способы из возможных - побитовый сдвиг для сериализации и побитовое "или с присваиванием" для десериализации. <br>
Максимально допустимый размер ключа - **64 килобайта** (65.535 байт).
Максимально допустимый размер значения - **4 гигабайта** (4.294.967.295 байт). <br>
Для очистки не актуальных данных предусмотрен механизм вакуумирования, который запускается через определённое количество записей. Количество записей можно указать при создании пространства данных. При вакуумировании создается временный файл, который является копией основного журнала, а журнал полностью перезаписывается. Этот метод помогает защитить данные, если в процессе очистки произошёл сбой.
При гипотетическом падении процесса пространство данных полностью восстанавливается из файла, выгружая данные в память.

## Установка

```sh
go get gitverse.ru/vestnik/dataspace
```

## Примеры основных возможностей

Инициализация пространства данных

```go
package main

import "gitverse.ru/vestnik/dataspace"

func main() {
	s, err := dataspace.New("test.dat", &dataspace.Config{
		Vacuum:           true,
		VacuumOperations: 100_000,
	})
	if err != nil {
		// обработка ошибки
	}
}
```

Запись значения

```go
s.Set([]byte("key"), []byte("value"))
```

Получение значения

```go
s.Get([]byte("key"))
```

Удаление значения

```go
s.Delete([]byte("key"))
```

Итерация значений

```go
// получение первых 10 элементов
s.Range(10, 0, dataspace.Asc)
// получение последних 10 элементов
s.Range(10, 0, dataspace.Desc)
// получение 5 элементов начиная с 10 элемента
s.Range(5, 10, dataspace.Asc)
// получение 5 элементов начиная с 10 элемента с конца
s.Range(5, 10, dataspace.Desc)
```

## Как использовать?

Данная технология отлично подходит для несложных запросов в которых важна большая скорость записи и чтения. Самая лучшая стратегия хранения данных - использовать пространство для **одной** модели данных, которая имеет миллионы значений. Создание инстанса для каждой модели позволит разделить списки и деревья, так же настроить вакуумирование файла под каждую модель индивидуально, в зависимости от интенсивности записи. <br>

## Roadmap

- Внедрение графов с основными возможностями, такими как поиск с помощью алгоритма A* (усовершенствованный алгоритм Дейкстры), выгрузка сети графов в JSON и т.д...
- Внедрение типа данных **sequence** для атомарных операций над целыми числами для реализации счётчиков.
- Ну и конечно:

```go
for {
    опитимизация
    тест
    if тест == успешно {
        continue
    }
}
```

Спасибо за просмотр!